/******************************************************************************

Title:

   Demo program to show the use of multiplexed seven segment module.

Description:
   Segment (a,b,c,d,e,f,c,dp connected to RD0,1,2,3,4,5,6,7)
   via 330 ohm resistors.

   Selection lines disp1(unit place),disp2,disp3,disp4(thousand place)

   connected to RA0,1,2,3

   The PIC18F4520 MCU Must be clocked by a 20MHz crystal

   For More information visit
   http://www.eXtremeElectronics.co.in
   (then search 'seven segment mux pic')


Author:
   Avinash Gupta.
   avinash@eXtremeElectronics.co.in

Copyright:
   eXtreme Electronics, India 2008- 2011

Notice:
   No part of this work can be copied or published in electronic or
   printed form without proper permission from the Original Creators.

   ONLY INTENDED FOR EDUCATIONAL, HOBBY AND PERSONAL USE.
   COMMERCIAL USE IS STRICTLY PROHIBITED.


Disclaimer of Warranty.

   THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
   EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
   PARTIES PROVIDE THE PROGRAM ?AS IS? WITHOUT WARRANTY OF ANY KIND, EITHER
   EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES

   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
   THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.
   SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
   SERVICING, REPAIR OR CORRECTION.

 ******************************************************************************/

#include <htc.h>

#include "types.h"

#define _XTAL_FREQ 20000000ul

//Chip Settings
__CONFIG(1, 0x0200);
__CONFIG(2, 0X1E1F);
__CONFIG(3, 0X8100);
__CONFIG(4, 0X0081);
__CONFIG(5, 0XC00F);


//Configure i/o ports where display is connected
//below is segment data port
#define SEVENSEG_TRIS   TRISD
#define SEVENSEG_PORT   PORTD

//below is disp select data port
#define SEVENSEG_CMN_TRIS  TRISA
#define SEVENSEG_CMN_PORT  PORTA

uint8_t digits[4] = {0, 0, 0, 0};


//Simple Delay Routine

void Wait(unsigned int delay) {
    for (; delay; delay--)
        __delay_us(100);
}

void SevenSegment(uint8_t num) {
    switch (num) {
        case 0:
            //  .GFEDCBA

            SEVENSEG_PORT = 0B00111111;
            break;

        case 1:
            //      .GFEDCBA
            SEVENSEG_PORT = 0B00000110;
            break;

        case 2:
            //      .GFEDCBA

            SEVENSEG_PORT = 0B01011011;
            break;

        case 3:
            //      .GFEDCBA
            SEVENSEG_PORT = 0B01001111;
            break;

        case 4:
            //      .GFEDCBA

            SEVENSEG_PORT = 0B01100110;
            break;

        case 5:
            //      .GFEDCBA
            SEVENSEG_PORT = 0B01101101;
            break;

        case 6:
            //      .GFEDCBA

            SEVENSEG_PORT = 0B01111101;
            break;
        case 7:
            //      .GFEDCBA
            SEVENSEG_PORT = 0B00000111;
            break;

        case 8:
            //      .GFEDCBA

            SEVENSEG_PORT = 0B01111111;
            break;

        case 9:
            //      .GFEDCBA
            SEVENSEG_PORT = 0B01101111;
            break;
    }
}

void SevenSegInit() {
    //Setup i/o ports
    SEVENSEG_TRIS = 0X00;
    SEVENSEG_CMN_TRIS = 0B11110000;

    SEVENSEG_CMN_PORT = 0B00000001;

    //Setup Timer0
    T0PS0 = 1; //Prescaler is divide by 256

    T0PS1 = 1;
    T0PS2 = 1;

    PSA = 0; //Timer Clock Source is from Prescaler

    T0CS = 0; //Prescaler gets clock from FCPU (5MHz)

    T08BIT = 1; //8 BIT MODE

    TMR0IE = 1; //Enable TIMER0 Interrupt
    PEIE = 1; //Enable Peripheral Interrupt

    GIE = 1; //Enable INTs globally

    TMR0ON = 1; //Now start the timer!
}

void SevenSegPrint(uint16_t num) {
    /*


    This function breaks apart a given integer into separete digits
    and writes them to the display array i.e. digits[]

     */
    uint8_t i = 0;
    uint8_t j;
    if (num > 9999) return;
    while (num) {
        digits[i] = num % 10;
        i++;

        num = num / 10;
    }
    for (j = i; j < 4; j++) digits[j] = 0;
}

void SevenSegISR() {
    /*

    This interrupt service routine (ISR)
    Updates the displays

     */

    TMR0 = 150;

    static uint8_t i;

    if (i == 3) {
        //If on last display then come
        //back to first.
        i = 0;

    } else
 {
        //Goto Next display
        i++;
    }



    //Activate a display according to i
    SEVENSEG_CMN_PORT = ((SEVENSEG_CMN_PORT & 0xf0) | (1 << i));

    //Write the digit[i] in the ith display.

    SevenSegment(digits[i]);
}

//Main Interrupt Service Routine (ISR)

void interrupt ISR() {
    //Check if it is TMR0 Overflow ISR
    if (TMR0IE && TMR0IF) {
        //Call the SevenSegISR
        SevenSegISR();

        //Clear Flag

        TMR0IF = 0;
    }
}

void main() {
    //Initialize the Seven Segment Subsystem
    SevenSegInit();

    //Print a number to display
    SevenSegPrint(1234);

    //Do nothing, just loop!

    while (1);

}

